//; ---------------------------------------------------------------------------
//; Object 18 - platforms	(GHZ, SYZ, SLZ)
//; ---------------------------------------------------------------------------

#include "platforms.h"

#include "../sprite.h"
#include "../objectsystemNEWER.h"
#include "../collision.h"
#include "../misc.h"

static object *obj;

#define player objects[0]
extern object *objects;

extern word v_limitbtm2;
extern byte f_switch[];
extern byte v_oscillate[];

//; ---------------------------------------------------------------------------
//; Sprite mappings - GHZ	platforms
//; ---------------------------------------------------------------------------
byte small[21] = {   4,
		 0xF4, 0xB, 0, 0x3B, 0xE0,    //; small platform
		 0xF4,   7, 0, 0x3F, 0xF8,
		 0xF4,   7, 0, 0x3F,    8,
		 0xF4,   3, 0, 0x47, 0x18
};
byte large[51] = { 0xA,
		 0xF4, 0xF, 0, 0xC5, 0xE0,    //; large column platform
		    4, 0xF, 0, 0xD5, 0xE0,
		 0x24, 0xF, 0, 0xD5, 0xE0,
		 0x44, 0xF, 0, 0xD5, 0xE0,
		 0x64, 0xF, 0, 0xD5, 0xE0,
		 0xF4, 0xF, 8, 0xC5,    0,
		    4, 0xF, 8, 0xD5,    0,
		 0x24, 0xF, 8, 0xD5,    0,
		 0x44, 0xF, 8, 0xD5,    0,
		 0x64, 0xF, 8, 0xD5,    0
};

byte *Map_Plat_GHZ[2] = { small, large };
//==========

void (*Plat_Index[5])() = { Plat_Main, Plat_Solid, Plat_Action2, Plat_Delete, Plat_Action };

//BasicPlatform:					; XREF: Obj_Index
void BasicPlatform(object *_obj)
{
    obj = _obj;

    //moveq	#0,d0
    //move.b	obRoutine(a0),d0
    byte routine = obj->Routine/2;

    //move.w	Plat_Index(pc,d0.w),d1
    //jmp	Plat_Index(pc,d1.w)
    (*Plat_Index[routine])();
}

//; ===========================================================================

//Plat_Main:	; Routine 0
void Plat_Main()
{
    //addq.b	#2,obRoutine(a0)
    obj->Routine = obj->Routine + 2;

    //move.w	#$4000,obGfx(a0)
    obj->Gfx = 0x4000;

    //move.l	#Map_Plat_GHZ,obMap(a0)
    obj->Map = Map_Plat_GHZ;

    //move.b	#$20,obActWid(a0)
    obj->ActionWidth = 32;

    //cmpi.b	#id_SYZ,(v_zone).w ; check if level is SYZ
    //bne.s	@notSYZ
    //move.l	#Map_Plat_SYZ,obMap(a0) ; SYZ specific code
    //move.b	#$20,obActWid(a0)
    //@notSYZ:
    //cmpi.b	#id_SLZ,(v_zone).w ; check if level is SLZ
    //bne.s	@notSLZ
    //move.l	#Map_Plat_SLZ,obMap(a0) ; SLZ specific code
    //move.b	#$20,obActWid(a0)
    //move.w	#$4000,obGfx(a0)
    //move.b	#3,obSubtype(a0)
    //@notSLZ:

    //move.b	#4,obRender(a0)
    obj->Render = 4;

    //move.b	#4,obPriority(a0)
    obj->Priority = 4;

    //move.w	obY(a0),$2C(a0)
    ptrsetobjvariableword(0x2C, obj, obj->Y);

    //move.w	obY(a0),$34(a0)
    ptrsetobjvariableword(0x34, obj, obj->Y);

    //move.w	obX(a0),$32(a0)
    ptrsetobjvariableword(0x32, obj, obj->X);

    //move.w	#$80,obAngle(a0)
    obj->Angle = 0x80;

    //moveq	#0,d1
    long startFrame = 0;

    //move.b	obSubtype(a0),d0
    byte platType = obj->Subtype;

    //cmpi.b	#$A,d0		; is object type $A (large platform)?
    //bne.s	@setframe	; if not, branch
    if (platType == 10)
    {
        //addq.b	#1,d1		; use frame #1
        startFrame += 1;
        
	//move.b	#$20,obActWid(a0) ; set width
        obj->ActionWidth = 32;
    }

    //@setframe:
    //move.b	d1,obFrame(a0)	; set frame to d1 
    obj->Frame = startFrame;

}

//; ===========================================================================

//Plat_Solid:	; Routine 2
void Plat_Solid()
{
    //tst.b	$38(a0)
    //beq.s	loc_7EE0
    if (obj->Variables[0x38-0x28] != 0)
    {
        //subq.b	#4,$38(a0)
        obj->Variables[0x38-0x28] = obj->Variables[0x38-0x28] - 4;
    }
    
    //loc_7EE0:
    //moveq	#0,d1
    //move.b	obActWid(a0),d1
    byte actionWidth = obj->ActionWidth;

    //bsr.w	PlatformObject
    PlatformObject(obj, actionWidth);

    Plat_Action();
}
  
//Plat_Action:	; Routine 8
void Plat_Action()
{
    //bsr.w	Plat_Move
    Plat_Move();

    //bsr.w	Plat_Nudge
    Plat_Nudge();

    //bsr.w	DisplaySprite
    DisplaySprite(obj);

    //bra.w	Plat_ChkDel
    Plat_ChkDel();
}
//; ===========================================================================

//Plat_Action2:	; Routine 4
void Plat_Action2()
{
    //cmpi.b	#$40,$38(a0)
    //beq.s	loc_7F06
    if (obj->Variables[0x38-0x28] != 0x40) 
    {
        //addq.b	#4,$38(a0)
        obj->Variables[0x38-0x28] = obj->Variables[0x38-0x28] + 4;        
    }
    
    //loc_7F06:
    //moveq	#0,d1
    //move.b	obActWid(a0),d1
    byte actionWidth = obj->ActionWidth;

    signed short distPlayerLPlatformX;
    //bsr.w	ExitPlatform
    ExitPlatform(obj, actionWidth, distPlayerLPlatformX);

    //move.w	obX(a0),-(sp)
    word oldX = obj->X;

    //bsr.w	Plat_Move
    Plat_Move();

    //bsr.w	Plat_Nudge
    Plat_Nudge();

    //move.w	(sp)+,d2
   
    //bsr.w	MvSonicOnPtfm2
    MvSonicOnPtfm2(obj, oldX); 

    //bsr.w	DisplaySprite
    DisplaySprite(obj);		

    //bra.w	Plat_ChkDel
    Plat_ChkDel();

}   //rts

//; ---------------------------------------------------------------------------
//; Subroutine to	move platform slightly when you	stand on it
//; ---------------------------------------------------------------------------

//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

//moves platform in the y direction at a sinusoid rate, dependent on a 'nudgeAngle' set at init and accumulated/decumulated at stand/leave respectively
//Plat_Nudge:				; XREF: Plat_Action; Plat_Action2
void Plat_Nudge()
{
    //move.b	$38(a0),d0
    byte nudgeAngle = obj->Variables[0x38-0x28];

    signed short sine, cosine;
    //bsr.w	CalcSine
    CalcSine(nudgeAngle, sine, cosine);

    //move.w	#$400,d1
    word scaleVal = 0x400;

    //muls.w	d1,d0
    //swap	d0
    signed short scaledSine = (sine * scaleVal)>>16;

    //add.w	$2C(a0),d0
    word newY = scaledSine + ptrgetobjvariableword(0x2C, obj);  //0x2C = current, shifted height of platform due to Plat_Move
		
    //move.w	d0,obY(a0)
    obj->Y = newY;
}   //rts	
//; End of function Plat_Nudge

//; ---------------------------------------------------------------------------
//; Subroutine to	move platforms
//; ---------------------------------------------------------------------------

//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

void (*index[13])() = { type00, type01, type02, type03, type04, type05, type06, type07, type08, type00, type0A, type0B, type0C };

//Plat_Move:				; XREF: Plat_Action; Plat_Action2
void Plat_Move()
{
    //moveq	#0,d0
    //move.b	obSubtype(a0),d0
    //andi.w	#$F,d0
    byte platType = obj->Subtype & 0xF;
		
    //add.w	d0,d0
    word platRoutine = platType; //no scaling needed in c

    //move.w	@index(pc,d0.w),d1
    //jmp	@index(pc,d1.w)
    (*index[platRoutine])();
}   //; End of function Plat_Move

//; ===========================================================================

//; ===========================================================================

//@type00:
void type00()
{
    //rts			; platform 00 doesn't move
}   //; ===========================================================================

//@type05:
void type05()
{
    //move.w	$32(a0),d0
    word startX = ptrgetobjvariableword(0x32, obj);

    //move.b	obAngle(a0),d1	; load platform-motion variable
    //neg.b	d1		; reverse platform-motion
    //addi.b	#$40,d1
    signed char shiftX = -obj->Angle + 0x40;

    //bra.s	@type01_move
    type01_move(startX, shiftX);

}   //; ===========================================================================

//@type01:
void type01()
{
    //move.w	$32(a0),d0
    word startX = ptrgetobjvariableword(0x32, obj);
		
    //move.b	obAngle(a0),d1	; load platform-motion variable
    //subi.b	#$40,d1
    signed char shiftX = obj->Angle - 0x40;

    type01_move(startX, shiftX);
}

//@type01_move:
void type01_move(word startX, signed char shiftX)
{
    //ext.w	d1
    signed short _shiftX = shiftX;

    //add.w	d1,d0
    word newX = startX + _shiftX;

    //move.w	d0,obX(a0)	; change position on x-axis
    obj->X = newX;

    //bra.w	@chgmotion
    chgmotion();             //update x-shift value with global oscillation values
}

//; ===========================================================================
//@type0C:
void type0C()
{
    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //move.b	(v_oscillate+$E).w,d1 ; load platform-motion variable
    signed char motion = v_oscillate[0xE];

    //neg.b	d1		; reverse platform-motion
    //addi.b	#$30,d1
    signed char shiftY = -motion + 0x30;
		
    //bra.s	@type02_move
    type02_move(startY, shiftY);

}   //; ===========================================================================

//@type0B:
void type0B()
{
    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //move.b	(v_oscillate+$E).w,d1 ; load platform-motion variable
    signed char motion = v_oscillate[0xE];

    //subi.b	#$30,d1
    signed char shiftY = motion - 0x30;
		
    //bra.s	@type02_move
    type02_move(startY, shiftY);

}   //; ===========================================================================

//@type06:
void type06()
{
    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //move.b	obAngle(a0),d1	; load platform-motion variable
    //neg.b	d1		; reverse platform-motion
    //addi.b	#$40,d1
    signed char shiftY = -obj->Angle + 0x40;                    //shift value in phase with type 01 & 05
		
    //bra.s	@type02_move
    type02_move(startY, shiftY);
}   //; ===========================================================================

//@type02:
void type02()
{
    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //move.b	obAngle(a0),d1	; load platform-motion variable
    //subi.b	#$40,d1
    signed char shiftY = obj->Angle - 0x40;                    //shift value in phase with type 01 & 05
		
    //bra.s	@type02_move
    type02_move(startY, shiftY);
}

void type02_move(word startY, signed char shiftY)
{
    //ext.w	d1
    signed short _shiftY = shiftY;

    //add.w	d1,d0
    word newY = startY + _shiftY;

    //move.w	d0,$2C(a0)	; change position on y-axis
    ptrsetobjvariableword(0x2C, obj, newY);

    //bra.w	@chgmotion
    chgmotion();

}   //; ===========================================================================

//@type03:
void type03()
{
    //tst.w	$3A(a0)		; is time delay	set?
    //bne.s	@type03_wait	; if yes, branch
    if (ptrgetobjvariableword(0x3A, obj) == 0)       //if time delay not set
    {
        //btst	#3,obStatus(a0)	; is Sonic standing on the platform?
        //beq.s	@type03_nomove	; if not, branch
        if ((obj->Status & 8) == 8)                  //and sonic standing on platform
        {
	    //move.w	#30,$3A(a0)	; set time delay to 0.5	seconds
            ptrsetobjvariableword(0x3A, obj, 30);
        }
	
        //@type03_nomove:
	//rts
    }
    else
    {	
        //@type03_wait:
	//subq.w	#1,$3A(a0)	; subtract 1 from time
        ptrsubobjvariableword(0x3A, obj, 1);
   
	//bne.s	@type03_nomove	; if time is > 0, branch
        if (ptrgetobjvariableword(0x3A, obj) == 0)
        {
	    //move.w	#32,$3A(a0)
            ptrsetobjvariableword(0x3A, obj, 32);

	    //addq.b	#1,obSubtype(a0) ; change to type 04 (falling)
            obj->Subtype = obj->Subtype + 1;
        }
    }   //rts
}   //; ===========================================================================

//@type04:
void type04()
{
    //tst.w	$3A(a0)
    //beq.s	@loc_8048
    if (ptrgetobjvariableword(0x3A, obj) != 0)       //if time delay set
    {
        //subq.w	#1,$3A(a0)
        ptrsubobjvariableword(0x3A, obj, 1);

	//bne.s	@loc_8048
        if (ptrgetobjvariableword(0x3A, obj) == 0)
        {
	    //btst	#3,obStatus(a0)
            //beq.s	@loc_8042
            if ((obj->Status & 8) == 8)
            {
		//bset	#1,obStatus(a1)
                player.Status = player.Status | 2;

		//bclr	#3,obStatus(a1)
                player.Status = player.Status - (player.Status & 8);

		//move.b	#2,obRoutine(a1)
                player.Routine = 2;

		//bclr	#3,obStatus(a0)
                obj->Status = obj->Status - (obj->Status & 8);

		//clr.b	$25(a0)
                obj->Angle = (obj->Angle & 0x00FF);

		//move.w	obVelY(a0),obVelY(a1)
                player.VelocityY = obj->VelocityY;
            }
	    
            //@loc_8042:
	    //move.b	#8,obRoutine(a0)
            obj->Routine = 8;
        }
    }
 
    //@loc_8048:
    //move.l	$2C(a0),d3
    long platY = ptrgetobjvariablelong(0x2C, obj);

    //move.w	obVelY(a0),d0
    //ext.l	d0
    //asl.l	#8,d0
    long platShiftY = obj->VelocityY*256;

    //add.l	d0,d3
    long newY = platY + platShiftY;

    //move.l	d3,$2C(a0)
    ptrsetobjvariablelong(0x2C, obj, newY);

    //addi.w	#$38,obVelY(a0)
    obj->VelocityY = obj->VelocityY + 0x38;

    //move.w	(v_limitbtm2).w,d0
    //addi.w	#$E0,d0
    word platYLimit = v_limitbtm2 + 0xE0;

    //cmp.w	$2C(a0),d0
    //bcc.s	@locret_8074
    if (platYLimit < ptrgetobjvariableword(0x2C, obj))
    {
        //move.b	#6,obRoutine(a0)
        obj->Routine = 6;
    }
	    
    //@locret_8074:
    //rts	
}   //; ===========================================================================

//@type07:
void type07()
{
    //tst.w	$3A(a0)		; is time delay	set?
    //bne.s	@type07_wait	; if yes, branch
    if (ptrgetobjvariableword(0x3A, obj) == 0)       //if time delay not set
    {
        //lea	(f_switch).w,a2	; load switch statuses
        byte *switchPtr = f_switch;

	//moveq	#0,d0
	//move.b	obSubtype(a0),d0 ; move object type ($x7) to d0
        byte platType = obj->Subtype;

	//lsr.w	#4,d0		; divide d0 by 8, round	down (16 actually lol)
        word switchIndex = platType / 16;

	//tst.b	(a2,d0.w)	; has switch no. d0 been pressed?
        //beq.s	@type07_nomove	; if not, branch
        if (switchPtr[switchIndex] != 0)
	{	
	    //move.w	#60,$3A(a0)	; set time delay to 1 second
            ptrsetobjvariableword(0x3A, obj, 60);
        }
	
        //@type07_nomove:
	//rts
    }
    else
    {	
        //@type07_wait:
	//subq.w	#1,$3A(a0)	; subtract 1 from time delay
        ptrsubobjvariableword(0x3A, obj, 1);

	//bne.s	@type07_nomove	; if time is > 0, branch
        if (ptrgetobjvariableword(0x3A, obj) == 0)
        {
	    //addq.b	#1,obSubtype(a0) ; change to type 08
	    obj->Subtype = obj->Subtype + 1;
        }
    }	//rts	
}   //; ===========================================================================

//@type08:
void type08()
{
    //subq.w	#2,$2C(a0)	; move platform	up
    ptrsubobjvariableword(0x2C, obj, 2);

    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //subi.w	#$200,d0
    signed short endY = startY - 0x200;

    //cmp.w	$2C(a0),d0	; has platform moved $200 pixels?
    //bne.s	@type08_nostop	; if not, branch
    if (ptrgetobjvariableword(0x2C, obj) == endY)
    {
        //clr.b	obSubtype(a0)	; change to type 00 (stop moving)
        obj->Subtype = 0;
    }
    
    //@type08_nostop:
    //rts	
}   //; ===========================================================================

//@type0A:
void type0A()
{
    //move.w	$34(a0),d0
    word startY = ptrgetobjvariableword(0x34, obj);

    //move.b	obAngle(a0),d1	; load platform-motion variable
    //subi.b	#$40,d1
    byte shiftY = obj->Angle - 0x40;

    //ext.w	d1
    //asr.w	#1,d1
    word shiftYScaled = shiftY/2;

    //add.w	d1,d0
    word newY = startY + shiftYScaled;

    //move.w	d0,$2C(a0)	; change position on y-axis
    ptrsetobjvariableword(0x2C, obj, newY);

    chgmotion();
}

void chgmotion()
{
    //@chgmotion:
    //move.b	(v_oscillate+$1A).w,$26(a0) ; update platform-movement variable
    obj->Angle = v_oscillate[0x1B];

}   //rts	

//; ===========================================================================

//Plat_ChkDel:				; XREF: Plat_Action; Plat_Action2
void Plat_ChkDel()
{
    //out_of_range.s	Plat_Delete,$32(a0)
    if (out_of_range(ptrgetobjvariableword(0x32, obj))) { Plat_Delete(); }

}   //rts
	
//; ===========================================================================

//Plat_Delete:	; Routine 6
void Plat_Delete()
{
    //bra.w	DeleteObject
    DeleteObject(obj);
}
